/*!
 * \file	shell.c
 * \brief	interactive shell 
 * \author	Alexis Lucazeau - lucaze_b@epitech.eu
 * \version	1.0
 * \date	10/23/2010 07:29:46 PM
 *
 * Intercative shell over HTTP
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>

#include "config.h"

int     usleep(unsigned int usec);

int     child_process(int *fd1, int *fd2, int *fd3, t_options *options)
{
    close(fd1[1]);
    close(fd2[0]);
    close(fd3[0]);

    if (fd1[0] != STDIN_FILENO)
    {
        if (dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO)
            perror("dup2");
        close(fd1[0]);
    }
    if (fd2[1] != STDOUT_FILENO)
    {
        if (dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO)
            perror("dup2");
        close(fd2[1]);
    }
    if (fd3[1] != STDERR_FILENO)
    {
        if (dup2(fd3[1], STDERR_FILENO) != STDERR_FILENO)
            perror("dup2");
        close(fd3[1]);
    }
    if (execl(options->shell, options->shellcmd, (char *)0) < 0)
    {
        perror("execl");
        return (1);
    }
    return(0);
}

int     get_biggerfd(int fd1, int fd2, int fd3, int fd4, int fd5)
{
    if (fd1 > fd2 && fd1 > fd3 && fd1 > fd4 && fd1 > fd5)
        return (fd1);
    if (fd2 > fd3 && fd2 > fd4 && fd2 > fd5)
        return (fd2);
    if (fd3 > fd4 && fd3 > fd5)
        return (fd3);
    if (fd4 > fd5)
        return (fd4);
    return (fd5);
}

int                 parent_process(int *fd1, int *fd2, int *fd3, t_options *options)
{
    int             size;
    char            out[BUFF_SIZE];
    char            in[BUFF_SIZE];
    int             rfd, wfd;
    fd_set          fdtab;
    fd_set          rfdtab;
    fd_set          wfdtab;
    struct timeval  timeout;

    timeout.tv_sec  = 0;
    timeout.tv_usec = 0;

    close(fd1[0]);
    close(fd2[1]);
    close(fd3[1]);
    rfd = create_fifo_file(options->input, O_RDONLY);
    wfd = create_fifo_file(options->output, O_RDWR);

    FD_ZERO(&fdtab);
    FD_SET(rfd, &fdtab);
    FD_SET(wfd, &fdtab);
    FD_SET(fd1[1], &fdtab);
    FD_SET(fd2[0], &fdtab);
    FD_SET(fd3[0], &fdtab);

    while (42)
    {
        FD_ZERO(&rfdtab);
        FD_ZERO(&wfdtab);
        memcpy(&rfdtab, &fdtab, sizeof(fdtab));
        memcpy(&wfdtab, &fdtab, sizeof(fdtab));

        if (select(get_biggerfd(rfd, wfd, fd1[1], fd2[0], fd3[0]) + 1,
                    &rfdtab, &wfdtab, NULL, &timeout) == -1)
        {
            perror("select");
            return (1);
        }
        if (FD_ISSET(rfd, &rfdtab) && FD_ISSET(fd1[1], &wfdtab))
        {
            size = read(rfd, in, BUFF_SIZE);
            if (size > 0)
                size = write(fd1[1], in, size);
        }
        if (FD_ISSET(fd2[0], &rfdtab) && FD_ISSET(wfd, &wfdtab))
        {
            size = read(fd2[0], out, BUFF_SIZE);
            if (size > 0)
                size = write(wfd, out, size);
        }
        if (FD_ISSET(fd3[0], &rfdtab) && FD_ISSET(wfd, &wfdtab))
        {
            size = read(fd3[0], out, BUFF_SIZE);
            if (size > 0)
                size = write(wfd, out, size);
        }
        usleep(100);
    }
    close(rfd);
    close(wfd);
    return (0);
}

int         start_pipe(t_options *options)
{
    int     fd1[2];
    int     fd2[2];
    int     fd3[2];
    pid_t   pid;

    if ((pipe(fd1) < 0) || (pipe(fd2) < 0) || (pipe(fd3) < 0))
    {
        perror("pipe");
        return (1);
    }
    if ((pid = fork()) < 0)
    {
        perror("fork");
        return (1);
    }
    else if (pid == 0)
        return (child_process(fd1, fd2, fd3, options));
    else
    {
        printf("executing shell: %s\n", options->shell);
        return (parent_process(fd1, fd2, fd3, options));
    }
    return (0);
}

int             main(int argc, char **argv, char **envp)
{
    t_options   options;

    if (get_options(argc, argv, envp, &options) != 0)
        return (EXIT_FAILURE);
    if (start_pipe(&options) != 0)
        return (EXIT_FAILURE);
    return (EXIT_SUCCESS);
}

